package genetic.q4.v1;

import java.util.LinkedList;
import java.util.List;

import genetic.Genotype;
import genetic.Phenotype;
import genetic.q4.EternityPiece;

public class EternityPhenotype extends Phenotype{



	@Override
	public Genotype createRandomInstance() {
		//		List<EternityPiece> gen = new LinkedList<EternityPiece>();
		//
		//		int color = EternityPiece.GRAY;
		//		List<EternityPiece> l = borderFilter(color, gen);;
		//
		//		boolean flag = l.size()>0;
		//
		//		for (int i=1; flag; i++){
		//			int random = (int)(Math.random()*(double)l.size());
		//			EternityPiece curPiece = l.remove(random);
		//			gen.add(curPiece);
		//
		//			if (i%15==1){
		//				color = curPiece.getColor(EternityPiece.GRAY, 1, EternityPiece.GRAY, 2);
		//			} else {
		//				color = curPiece.getColor(color, 1, EternityPiece.GRAY, 2);
		//				//					System.out.println(curPiece + " " + color);
		//			}
		//			l = borderFilter(color, gen);
		//			if (i%15==0){
		//				l = filter(l, EternityPiece.GRAY, 3, EternityPiece.GRAY, gen);	//filter again to get corners only
		//			} else {
		//				l = filterOut(l, EternityPiece.GRAY, 3, EternityPiece.GRAY, gen);	//filter again to get corners only
		//			}
		//			flag = l.size()>0;
		//		}
		//		return new EternityGenotype(gen);
		return new EternityGenotype(fillUpFrame(new LinkedList<EternityPiece>()));
	}

	public List<EternityPiece> fillUpFrame(List<EternityPiece> gen){
		List<EternityPiece> l;
		int color;

		int size = gen.size();
		if (size==0){
			color = EternityPiece.GRAY;
		} else {
			EternityPiece p = gen.get(size-1);
			if (size%15==1){
				color = p.getColor(EternityPiece.GRAY, 1, EternityPiece.GRAY, 2);
			} else {
				color = p.getColor(EternityPiece.GRAY, 1);
			}
		}
		l = borderFilter(color, gen);
		if (size%15==0){
			l = filter(l, EternityPiece.GRAY, 3, EternityPiece.GRAY, gen);	//filter again to get corners only
		} else {
			l = filterOut(l, EternityPiece.GRAY, 3, EternityPiece.GRAY, gen);	//filter again to get corners only
		}

		boolean flag = l.size() > 0;
		for (int i=(size+1); flag; i++){
			int random = (int)(Math.random()*l.size());
			EternityPiece curPiece = l.remove(random);
			gen.add(curPiece);

			if (i%15==1){
				color = curPiece.getColor(EternityPiece.GRAY, 1, EternityPiece.GRAY, 2);
			} else {
				color = curPiece.getColor(EternityPiece.GRAY, 1);
				//					System.out.println(curPiece + " " + color);
			}
			l = borderFilter(color, gen);
			if (i%15==0){
				l = filter(l, EternityPiece.GRAY, 3, EternityPiece.GRAY, gen);	//filter again to get corners only
			} else {
				l = filterOut(l, EternityPiece.GRAY, 3, EternityPiece.GRAY, gen);	//filter again to get corners only
			}
			flag = l.size()>0;
		}
		return gen;
	}

	@Override
	public Genotype[] crossover(Genotype g1, Genotype g2) {
		EternityGenotype eg1 = (EternityGenotype)(g1);
		EternityGenotype eg2 = (EternityGenotype)(g2);

		List<EternityPiece> l1 = new LinkedList<EternityPiece>();
		List<EternityPiece> l2 = new LinkedList<EternityPiece>();

		//copy some of the first parent
		int rand = (int)(Math.random()*(eg1.getSize()-1))+1;
		for (int i=0; i<rand; i++){
			l1.add(eg1.getPiece(i));
		}
		rand = (int)(Math.random()*(eg2.getSize()-1))+1;
		for (int i=0; i<rand; i++){
			l2.add(eg2.getPiece(i));
		}
		//copy the rest using the second parent, or random if the piece is unavailable
		boolean flag = true;
		while(flag){
			int index = eg2.indexOf(l1.get(l1.size()-1));
			boolean tmp = ((index+1) % 15 == 0);
			boolean tmp2 = (l1.size() % 15 == 0);
			if ((index>0) && (index<eg2.getSize()-1) && ((tmp && tmp2) || (!tmp && !tmp2)) && !l1.contains(eg2.getPiece(index+1))){
				l1.add(eg2.getPiece(index+1));
			} else {
				flag = insertRandom(l1);
			}
//			System.out.println(l1.toString());
		}
		flag = true;
		while(flag){
			int index = eg1.indexOf(l2.get(l2.size()-1));
			boolean tmp = ((index+1) % 15 == 0);
			boolean tmp2 = (l2.size() % 15 == 0);
			if (index>0 && index<eg1.getSize()-1 && ((tmp && tmp2) || (!tmp && !tmp2)) && !l2.contains(eg1.getPiece(index+1))){
				l2.add(eg1.getPiece(index+1));
			} else {
				flag = insertRandom(l2);
			}
		}
		return new Genotype[]{new EternityGenotype(l1), new EternityGenotype(l2)};
	}

	private boolean insertRandom(List<EternityPiece> l1){
		int size = l1.size();
		EternityPiece p = l1.get(size-1);
		int color;
		if (size%15==1){
			color = p.getColor(EternityPiece.GRAY, 1, EternityPiece.GRAY, 2);
		} else {
			color = p.getColor(EternityPiece.GRAY, 1);
		}
		List<EternityPiece> l = borderFilter(color, l1);
		if (size%15==0){
			l = filter(l, EternityPiece.GRAY, 3, EternityPiece.GRAY, l1);	//filter again to get corners only
		} else {
			l = filterOut(l, EternityPiece.GRAY, 3, EternityPiece.GRAY, l1);	//filter again to get corners only
		}
		boolean flag = l.size()>0;
		if (flag){
			int random = (int)(Math.random()*l.size());
			l1.add(l.remove(random));
		}
		return flag;
	}

	@Override
	public double fitness(Genotype g) {
		return ((EternityGenotype)g).getFitness();
	}

	@Override
	public List<Genotype> getInitialPopulation(int populationSize) {
		return getInitialRandomPopulation(populationSize);
	}

	@Override
	public List<Genotype> getMatingPool(List<Genotype> population, int populationSize) {
		return getMatingPoolRouletteWheel(population, populationSize);
	}

	@Override
	public Genotype mutate(Genotype g, double Pm) {
		if (Math.random()<Pm){
			EternityGenotype eg = (EternityGenotype)g;
			double third = (eg.getSize()/3.0);
			int rand = (int)(Math.random()*third+2*third);
			List<EternityPiece> newList = new LinkedList<EternityPiece>();
			for (int i=0; i<rand; i++){
				newList.add(eg.getPiece(i));
			}
			return new EternityGenotype(fillUpFrame(newList));
		} else return g;
	}

	@Override
	public void updateFitness(List<Genotype> population) {
	}

	private List<EternityPiece> borderFilter(int requestedColor, List<EternityPiece> badList){
		return filter(EternityPiece.colormap.get(EternityPiece.GRAY), requestedColor, 1, EternityPiece.GRAY, badList);
	}

	private List<EternityPiece> filter(List<EternityPiece> org, int baseColor, int offset, int requestedColor, List<EternityPiece> badList){
		List<EternityPiece> ans = new LinkedList<EternityPiece>();
		for (EternityPiece e: org){
			if (!badList.contains(e) && e.fits(baseColor, offset, requestedColor)){
				ans.add(e);
			}
		}
		return ans;
	}

	private List<EternityPiece> filterOut(List<EternityPiece> org, int baseColor, int offset, int requestedColor, List<EternityPiece> badList){
		List<EternityPiece> ans = new LinkedList<EternityPiece>();
		for (EternityPiece e: org){
			if (!badList.contains(e) && !e.fits(baseColor, offset, requestedColor)){
				ans.add(e);
			}
		}
		return ans;
	}

//	public static void main(String[] args) {
//		for (int i=0; i<20; i++){
//			EternityGenotype p1 = (EternityGenotype)(new EternityPhenotype().createRandomInstance());
//			EternityGenotype p2 = (EternityGenotype)(new EternityPhenotype().createRandomInstance());
////			p = (EternityGenotype)(new EternityPhenotype().mutate(p, 1));
//			Genotype[] co = new EternityPhenotype().crossover(p1, p2);
//			EternityGenotype p = (EternityGenotype)co[0];
//			System.out.println(p.rep.size() + " - " + p.rep.toString());
//			p = (EternityGenotype)co[1];
//			System.out.println(p.rep.size() + " - " + p.rep.toString());
//		}
//	}

}
